<template>
  <div class="main">
    <div class="left">
      <div id="stencil"></div>
    </div>
    <div class="right">
      <div class="topTool">
        <div class="tool" @click="undo">
          <i class="el-icon-refresh-left"></i>
          <div>撤 销</div>
        </div>

        <div class="tool" @click="clear">
          <i class="el-icon-refresh"></i>
          <div>清 空</div>
        </div>

        <div class="tool" @click="downImg">
          <i class="el-icon-picture-outline"></i>
          <div>导出图片</div>
        </div>
        <div class="tool" @click="save">
          <i class="el-icon-finished"></i>
          <div>保 存</div>
        </div>
      </div>
      <div id="container"></div>
    </div>
    <el-drawer
      title="节点编辑"
      size="17%"
      :visible.sync="drawer"
      :direction="direction"
    >
      <div class="drawerContent">
        <el-form
          ref="form"
          :model="clickNode.customAttrs"
          label-width="80px"
          size="mini"
        >
          <el-form-item label="节点内容">
            <el-input
              v-model="clickNode.customAttrs.clickNodeContent"
            ></el-input>
          </el-form-item>
          <el-form-item label="尺寸">
            <el-row>
              <el-col :span="11"
                ><el-input-number
                  v-model="clickNode.customAttrs.clickNodeBoxSizeWidth"
                  controls-position="right"
                  :min="1"
                  :max="500"
                  style="width: 90px"
                ></el-input-number
              ></el-col>
              <el-col :span="11"
                ><el-input-number
                  v-model="clickNode.customAttrs.clickNodeBoxSizeHeight"
                  controls-position="right"
                  :min="1"
                  :max="500"
                  style="width: 90px"
                ></el-input-number
              ></el-col>
            </el-row>
          </el-form-item>
          <el-form-item label="位置">
            <el-row>
              <el-col :span="11"
                ><el-input-number
                  v-model="clickNode.customAttrs.positionx"
                  controls-position="right"
                  :min="1"
                  :max="500"
                  style="width: 90px"
                ></el-input-number
              ></el-col>
              <el-col :span="11"
                ><el-input-number
                  v-model="clickNode.customAttrs.positiony"
                  controls-position="right"
                  :min="1"
                  :max="500"
                  style="width: 90px"
                ></el-input-number
              ></el-col>
            </el-row>
          </el-form-item>
          <el-form-item label="字号">
            <el-row>
              <el-col :span="11"
                ><el-input-number
                  v-model="clickNode.customAttrs.clickNodeContentSize"
                  controls-position="right"
                  :min="1"
                  :max="500"
                  style="width: 90px"
                ></el-input-number
              ></el-col>
              <el-col :span="4">
                <el-color-picker
                  v-model="clickNode.customAttrs.clickNodeTextColor"
                ></el-color-picker>
              </el-col>
            </el-row>
          </el-form-item>
          <el-form-item label="填充">
            <el-row>
              <el-col :span="4">
                <el-color-picker
                  v-model="clickNode.customAttrs.clickNodeBgColor"
                ></el-color-picker>
              </el-col>
            </el-row>
          </el-form-item>
          <el-form-item label="边框">
            <el-row>
              <el-col :span="4">
                <el-color-picker
                  v-model="clickNode.customAttrs.clickNodeBorderColor"
                ></el-color-picker>
              </el-col>
            </el-row>
          </el-form-item>
        </el-form>
        <div class="myBtns">
          <el-button
            type="primary"
            size="mini"
            @click="
              addOrSubOrColorClickNodeData('add', 'clickNodeBoxSizeWidth')
            "
            >保 存</el-button
          >
          <el-button type="danger" size="mini" @click="drawer = false"
            >取 消</el-button
          >
        </div>
      </div>
    </el-drawer>

    <!-- <el-button
      @click="addOrSubOrColorClickNodeData('add', 'clickNodeBoxSizeWidth')"
      size="mini"
      icon="el-icon-caret-top"
      class="mini-icon"
      @mousedown.native="holdDown('add', 'clickNodeBoxSizeWidth')"
      @mouseup.native="holdUp()"
      >修改</el-button
    > -->
  </div>
</template>

<script>
import { Graph, Shape, Addon, DataUri } from "@antv/x6";
export default {
  name: "test2",

  data() {
    return {
      stencil: null, //左侧菜单对象
      graph: null, //画布
      timeEnd: null,
      time: null,
      clickNode: {
        //点击节点对象
        customAttrs: {
          clickNodeContent: "", // 内容
          clickNodeContentSize: "", // 字体大小
          clickNodeBoxSizeWidth: "", // 盒子宽度
          clickNodeBoxSizeHeight: "", // 盒子高度
          clickNodeBgColor: "", // 背景颜色
          clickNodeTextColor: "", // 文字颜色
          clickNodeBorderColor: "", // 边框颜色
          positionx: 0, // x位置
          positiony: 0, // y位置
        },
      },
      clickNodeTemp: null,
      drawer: false, //节点编辑抽屉
      direction: "rtl", //抽屉从右往左打开
    };
  },

  mounted() {
    this.initStencil();
    this.initCustomGraph();
    this.loadInitData();
    this.initShortcuts();
  },

  methods: {
    //初始化画布
    initCanvas() {
      // 初始化画布
      this.graph = new Graph({
        container: document.getElementById("container"),
        grid: true,
        width: 1660, // 这个是自己定义的宽度
        height: 880, // 这个是自己定义的高度
        background: {
          color: "#fff", // 设置画布背景颜色
        },
        mousewheel: {
          enabled: true,
          zoomAtMousePosition: true,
          modifiers: "ctrl",
          minScale: 0.5,
          maxScale: 3,
        },
        panning: {
          enabled: true, // 单独开启拖动操作
          modifiers: "shift", // 按下shift才可以拖动
        },
        connecting: {
          router: {
            name: "manhattan",
            args: {
              padding: 1,
            },
          },
          connector: {
            name: "rounded",
            args: {
              radius: 8,
            },
          },
          anchor: "center",
          connectionPoint: "anchor",
          allowBlank: true,
          snap: true,
          createEdge() {
            return new Shape.Edge({
              attrs: {
                line: {
                  stroke: "#A2B1C3",
                  strokeWidth: 2,
                  targetMarker: {
                    name: "block",
                    width: 12,
                    height: 8,
                  },
                },
              },
              zIndex: 0,
            });
          },
          validateConnection({ targetMagnet }) {
            return !!targetMagnet;
          },
        },
        highlighting: {
          magnetAdsorbed: {
            name: "stroke",
            args: {
              attrs: {
                fill: "#5F95FF",
                stroke: "#5F95FF",
              },
            },
          },
        },
        resizing: true,
        rotating: true,
        selecting: {
          enabled: true,
          rubberband: true,
          showNodeSelectionBox: true,
        },
        snapline: true,
        keyboard: true,
        clipboard: true,
      });
    },
    //初始化左侧图行菜单
    initStencil() {
      this.initCanvas();
      this.stencil = new Addon.Stencil({
        // 这里注释掉的原因如下图
        target: this.graph,
        stencilGraphWidth: 200,
        stencilGraphHeight: 180,
        groups: [
          {
            title: "基础流程图",
            name: "group1",
          },
        ],
        layoutOptions: {
          columns: 2,
          columnWidth: 80,
          rowHeight: 55,
        },
      });
      let appendChildTemp = document.getElementById("stencil");
      if (appendChildTemp) {
        appendChildTemp.appendChild(this.stencil.container);
      }
    },
    //初始化自定义图形
    initCustomGraph() {
      //初始化连接线能连接的点
      const ports = {
        groups: {
          top: {
            position: "top",
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
                style: {
                  // 设置隐藏,在通过事件鼠标移动显示
                  visibility: "hidden",
                },
              },
            },
          },
          right: {
            position: "right",
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
                style: {
                  visibility: "hidden",
                },
              },
            },
          },
          bottom: {
            position: "bottom",
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
                style: {
                  visibility: "hidden",
                },
              },
            },
          },
          left: {
            position: "left",
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
                style: {
                  visibility: "hidden",
                },
              },
            },
          },
        },
        items: [
          {
            group: "top",
          },
          {
            group: "right",
          },
          {
            group: "bottom",
          },
          {
            group: "left",
          },
        ],
      };
      Graph.registerNode(
        "custom-polygon",
        {
          inherit: "polygon",
          width: 66,
          height: 36,
          markup: [
            {
              tagName: "polygon",
              selector: "body",
            },
            {
              tagName: "text",
              selector: "label",
            },
          ],
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {
            ...ports,
            items: [
              // 这里是限制连接点多少个的地方
              {
                group: "top",
              },
              {
                group: "bottom",
              },
            ],
          },
        },
        true
      );

      Graph.registerNode(
        "custom-circle",
        {
          inherit: "circle",
          width: 45,
          height: 45,
          markup: [
            {
              tagName: "circle",
              selector: "body",
            },
            {
              tagName: "text",
              selector: "label",
            },
          ],
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: { ...ports },
        },
        true
      );

      Graph.registerNode(
        "custom-rect",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          markup: [
            {
              tagName: "rect",
              selector: "body",
            },
            {
              tagName: "text",
              selector: "label",
            },
          ],
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: { ...ports },
        },
        true
      );
      const r1 = this.graph.createNode({
        shape: "custom-rect",
        label: "开始",
        attrs: {
          body: {
            rx: 20,
            ry: 26,
          },
        },
      });
      const r2 = this.graph.createNode({
        shape: "custom-rect",
        label: "过程",
      });
      const r3 = this.graph.createNode({
        shape: "custom-rect",
        attrs: {
          body: {
            rx: 6,
            ry: 6,
          },
        },
        label: "可选过程",
      });
      const r4 = this.graph.createNode({
        shape: "custom-polygon",
        attrs: {
          body: {
            refPoints: "0,10 10,0 20,10 10,20",
          },
        },
        label: "决策",
      });
      const r5 = this.graph.createNode({
        shape: "custom-polygon",
        attrs: {
          body: {
            refPoints: "10,0 40,0 30,20 0,20",
          },
        },
        label: "数据",
      });
      const r6 = this.graph.createNode({
        shape: "custom-circle",
        label: "连接",
      });
      this.stencil.load([r1, r2, r3, r4, r5, r6], "group1");

      const showPorts = (ports, show) => {
        for (let i = 0, len = ports.length; i < len; i = i + 1) {
          ports[i].style.visibility = show ? "visible" : "hidden";
        }
      };

      // 移入节点
      this.graph.on("node:mouseenter", () => {
        const container = document.getElementById("container"); // 这里获取id 是你画板的id
        if (container) {
          const ports = container.querySelectorAll(".x6-port-body");
          showPorts(ports, true);
        }
      });

      // 移出节点
      this.graph.on("node:mouseleave", () => {
        const container = document.getElementById("container"); // 这里获取id 是你画板的id
        if (container) {
          const ports = container.querySelectorAll(".x6-port-body");
          showPorts(ports, false);
        }
      });
    },
    //快捷键和事件
    initShortcuts() {
      this.graph.bindKey(["meta+c", "ctrl+c"], () => {
        const cells = this.graph.getSelectedCells();
        if (cells.length) {
          this.graph.copy(cells);
        }
        return false;
      });

      this.graph.bindKey(["meta+x", "ctrl+x"], () => {
        const cells = this.graph.getSelectedCells();
        if (cells.length) {
          this.graph.cut(cells);
        }
        return false;
      });
      this.graph.bindKey(["meta+v", "ctrl+v"], () => {
        if (!this.graph.isClipboardEmpty()) {
          const cells = this.graph.paste({ offset: 32 });
          this.graph.cleanSelection();
          this.graph.select(cells);
        }
        return false;
      });

      //undo redo
      this.graph.bindKey(["meta+z", "ctrl+z"], () => {
        if (this.graph.history.canUndo()) {
          this.graph.history.undo();
        }
        return false;
      });
      this.graph.bindKey(["meta+shift+z", "ctrl+shift+z"], () => {
        if (this.graph.history.canRedo()) {
          this.graph.history.redo();
        }
        return false;
      });

      // select all
      this.graph.bindKey(["meta+shift+a", "ctrl+shift+a"], () => {
        const nodes = this.graph.getNodes();
        if (nodes) {
          this.graph.select(nodes);
        }
      });

      //delete
      this.graph.bindKey("backspace", () => {
        const cells = this.graph.getSelectedCells();
        // console.log(cells.isEdge())
        // if (cells.length) {
        //   graph.removeCells(cells)
        // }
      });

      // zoom
      this.graph.bindKey(["ctrl+1", "meta+1"], () => {
        const zoom = this.graph.zoom();
        if (zoom < 1.5) {
          this.graph.zoom(0.1);
        }
      });
      this.graph.bindKey(["ctrl+2", "meta+2"], () => {
        const zoom = this.graph.zoom();
        if (zoom > 0.5) {
          this.graph.zoom(-0.1);
        }
      });

      // 调整节点大小事件
      this.graph.on("node:resized", ({ e, x, y, node, view }) => {
        this.setClickNode(node); // 这里是将节点保存到变量的方法
      });

      // 移动节点事件
      this.graph.on("node:moved", ({ e, x, y, node, view }) => {});

      // 点击背景板
      this.graph.on("blank:click", ({ e, x, y }) => {});

      // 点击节点
      this.graph.on("node:click", ({ e, x, y, node, view }) => {
        this.setClickNode(node);
        this.drawer = true;
      });

      // 添加边事件, 这里是添加线的删除事件
      this.graph.on("edge:added", ({ edge, index, options }) => {
        edge.addTools([
          {
            name: "button-remove", // 添加删除按钮
            args: {
              distance: "85%",
              attrs: {
                y: -50,
                width: 20,
                height: 20,
              },
            },
          },
        ]);
      });
      // 拖动新增的事件
      this.graph.on("node:added", ({ node }) => {
        node.addTools([
          {
            name: "button-remove",
            args: {
              distance: "85%",
              y: -10, // 删除按钮的位置
              x: "100%",
            },
          },
          {
            name: "boundary",
            args: {
              // distance: 20,
              padding: 15,
            },
          },
        ]);
      });
    },
    //获取点击节点
    setClickNode(node) {
      this.clickNodeTemp = node; // 指向节点
      this.clickNode.customAttrs.clickNodeContent = node.attrs.text.text;
      this.clickNode.customAttrs.clickNodeContentSize =
        node.attrs.text.fontSize;
      this.clickNode.customAttrs.clickNodeTextColor = node.attrs.text.fill;
      const propTemp = this.clickNodeTemp.getProp(); // 获取样式信息
      this.clickNode.customAttrs.clickNodeBoxSizeWidth = propTemp.size.width;
      this.clickNode.customAttrs.clickNodeBoxSizeHeight = propTemp.size.height;
      this.clickNode.customAttrs.clickNodeBorderColor = node.attrs.body.stroke;
      this.clickNode.customAttrs.clickNodeBgColor = node.attrs.body.fill;

      const relativePos = node.position({ relative: true }); // 获取位置信息
      this.clickNode.customAttrs.positionx = relativePos.x;
      this.clickNode.customAttrs.positiony = relativePos.y;
    },
    // 鼠标按下时触发
    holdDown(addOrSubOrColorTemp, data) {
      var i = 0; //变量i
      this.timeEnd = setTimeout(() => {
        this.time = setInterval(() => {
          //setInterval可一直执行内部函数
          this.addOrSubOrColorClickNodeData(addOrSubOrColorTemp, data);
          i++; //若过一秒，执行一次i++
        }, 200);
      }, 800);

      if (i == 0) {
        //i=0时证明无长按事件为单击事件
        this.addOrSubOrColorClickNodeData(addOrSubOrColorTemp, data);
      }
    },

    holdUp() {
      clearTimeout(this.timeEnd);
      clearInterval(this.time); //如果按下时间不到1000毫秒便弹起，
    },

    // 加一减一或者颜色修改 addOrSubOrColorTemp 判断是点击加还是减  或者是选择颜色的
    addOrSubOrColorClickNodeData(addOrSubOrColorTemp, data) {
      if (addOrSubOrColorTemp == "add") {
        this.clickNode[data]++;
      } else if (addOrSubOrColorTemp == "sub") {
        this.clickNode[data]--;
      }
      (this.clickNode.customAttrs.positionx =
        this.clickNode.customAttrs.positionx * 1),
        (this.clickNode.customAttrs.positiony =
          this.clickNode.customAttrs.positiony * 1);
      this.clickNodeTemp.updateAttrs(this.clickNode);
      if (this.clickNodeTemp.setAttrs) {
        // 用这个方法设置颜色样式
        this.clickNodeTemp.setAttrs({
          body: {
            fill: this.clickNode.customAttrs.clickNodeBgColor,
            stroke: this.clickNode.customAttrs.clickNodeBorderColor,
          },
          label: {
            fill: this.clickNode.customAttrs.clickNodeTextColor,
          },
          text: {
            text: this.clickNode.customAttrs.clickNodeContent,
            fontSize: this.clickNode.customAttrs.clickNodeContentSize,
          },
        });

        if (addOrSubOrColorTemp && addOrSubOrColorTemp !== "color") {
          // 用这个方法修改长宽, 位置
          this.clickNodeTemp.setProp({
            size: {
              width: this.clickNode.customAttrs.clickNodeBoxSizeWidth,
              height: this.clickNode.customAttrs.clickNodeBoxSizeHeight,
            },
            position: {
              x: this.clickNode.customAttrs.positionx * 1,
              y: this.clickNode.customAttrs.positiony * 1,
            },
          });
        }
      }
      this.drawer = false;
    },
    //撤销
    undo() {
      if (!this.graph.isHistoryEnabled()) {
      }
      //   if (this.graph.isHistoryEnabled()) {
      //     this.graph.disableHistory();
      //   } else {
      //     this.graph.enableHistory();
      //   }
    },
    //清空
    clear() {
      this.$confirm("是否确定清空当前画布内容?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        this.graph.fromJSON({});
      });
    },
    //导出图片
    downImg() {
      this.graph.toPNG(
        (dataUri) => {
          // 下载
          DataUri.downloadDataUri(dataUri, "chart.png");
        },
        {
          padding: {
            top: 30,
            right: 30,
            bottom:30,
            left: 30,
          },
        }
      );
    },
    //保存
    save() {
      //页面图形转换成json
      let preservationData = this.formatGraphData();
      localStorage.setItem("graphCacheData", JSON.stringify(preservationData));
      console.log(preservationData);
    },
    //格式化最终保存数据
    formatGraphData() {
      let data = this.graph.toJSON();
      const model = {
        nodes: [],
        edges: [],
      };
      let tmp;
      if (data.cells) {
        tmp = data.cells;
      } else if (data.nodes || data.edges) {
        tmp = [].concat(data.nodes, data.edges);
      }
      if (tmp) {
        tmp.forEach((item) => {
          if (item.shape !== "edge") {
            model.nodes.push(item);
          } else {
            let sourceId = item.source;
            let targetId = item.target;
            model.edges.push({
              source: sourceId,
              target: targetId,
              connector: item.connector,
              attrs: item.attrs,
              router: item.router,
              labels: item.labels,
            });
          }
        });
      }
      return model;
    },
    //加载默认数据
    loadInitData() {
      var obj = JSON.parse(
        localStorage.getItem("graphCacheData") || '{"nodes":[],"edges":[]}'
      );
      if (obj && (obj.edges.length > 0 || obj.nodes.length > 0)) {
        let graphCacheData = JSON.parse(localStorage.getItem("graphCacheData"));
        this.graph.fromJSON(graphCacheData);
      }
    },
  },
};
</script>

<style scoped>
.main {
  display: flex;
}

#stencil {
  width: 240px;
  height: 100%;
}

.right {
  width: calc(100% - 260px);
}
.topTool {
  width: 100%;
  height: 45px;
  padding: 0 10px;
  display: flex;
}
.tool {
  min-width: 40px;
  cursor: pointer;
  color: #5f95ff;
  margin: 0 5px;
}
.tool:hover {
  color: #bdcef1;
}
.tool > div {
  font-size: 13px;
}
.title {
  width: 100%;
  height: 32px;
  line-height: 32px;
  background: #ededed;
  font-size: 13px;
  color: #666;
  font-weight: bold;
}
.drawerContent {
  width: 100%;
  padding: 10px 20px 10px 5px;
}
</style>
<style>
.x6-widget-stencil {
  width: 240px !important;
}
</style>